Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: metamask support #194

Merged
merged 23 commits into from
Jun 13, 2022
Merged

feat: metamask support #194

merged 23 commits into from
Jun 13, 2022

Conversation

YasunoriMATSUOKA
Copy link
Contributor

@YasunoriMATSUOKA YasunoriMATSUOKA commented May 27, 2022

@jununifi
Because it seems your reference PR's branch ( #188 and https://github.com/UnUniFi/web-apps/tree/metamask_integration ) was checked out from main branch, I have created new branch and new PR with checking out from develop branch. I will implement this feature here.

Todos

  • Install web3.js and MetaMask related packages and resolve build errors
  • Connect to MetaMask
  • Get public key from MetaMask
  • Check the public key derived from MetaMask signed signature is correct
  • Get UnUniFi bech32Prefix address
  • Check the UnUniFi bech32Prefix address derived from public key is correct
  • Sign with MetaMask
  • Check the signature is correct.
  • JSON.stringify with sorted args. ... This is @cosmos-client/core 's implementation. Update tx-builder.ts cosmos-client/cosmos-client-ts#70
  • Sign to Cosmos SDK transaction typed data ... I have implemented at once, but, as a result, I didn't use this.
  • Check the signature is correct.
  • Broadcast signed transaction data to UnUniFi network
  • merge feat: metamask successfully sent #212
  • resolve conflicts
  • Fix config.js
  • Check other tx result ... other than bank send tx. if necessary fix them. (but, I guess no fix will be necessary)
  • Disable MetaMask related feature at once to merge into develop -> Currently, this is not necessary. Before merging to main, this will be necessary.

Screenshots

Connect to MetaMask Image
ununifi-connect-metamask-sample_20220527

@YasunoriMATSUOKA YasunoriMATSUOKA added the enhancement New feature or request label May 27, 2022
@YasunoriMATSUOKA YasunoriMATSUOKA self-assigned this May 27, 2022
@YasunoriMATSUOKA YasunoriMATSUOKA marked this pull request as draft May 27, 2022 11:37
@YasunoriMATSUOKA
Copy link
Contributor Author

@kimurayu45z @jununifi
I think the feature to connect MetaMask and get public key and derive necessary account info may be completed.
Tomorrow, I will check the derived account info is correct or not, and after that, I will try to sign to Cosmos SDK transaction type data and broadcast it.

Screenshot of connecting MetaMask
ununifi-connect-metamask-sample_20220530

@YasunoriMATSUOKA
Copy link
Contributor Author

@kimurayu45z @jununifi
I think the feature to sign to tx with MetaMask and broadcast may be completed.
But, signature verification failed.
(I guess this is expected behavior because the public key derived from MetaMask and the address derived from the public key are wrong.)

Screenshot of sending tx with MetaMask
ununifi-bank-send-with-metamask-sample_20220531

tx post response

{
  "tx_response": {
    "height": "0",
    "txhash": "9BD1175811779EF0766C7254EDA8D0174669C533B2FF7A4B38EC2EA5DB3059A2",
    "codespace": "sdk",
    "code": 4,
    "data": "",
    "raw_log": "signature verification failed; please verify account number (2) and chain-id (test): unauthorized",
    "logs": [
    ],
    "info": "",
    "gas_wanted": "81448",
    "gas_used": "51368",
    "tx": null,
    "timestamp": "",
    "events": [
    ]
  }
}

chain's log (log level = debug)

8:32PM DBG indexed block txs height=794 module=txindex num_txs=0
8:32PM DBG served RPC HTTP response duration=0 method=OPTIONS module=api-server remoteAddr=172.30.240.1:11821 status=200 url={"ForceQuery":false,"Fragment":"","Host":"","Opaque":"","Path":"/cosmos/tx/v1beta1/txs","RawFragment":"","RawPath":"","RawQuery":"","Scheme":"","User":null}
signBytes 
�
�
/cosmos.bank.v1beta1.MsgSendl
.ununifi1xzy2k2u7fcwpgm0fgqzr8jl95efy93w0tsd4tr.ununifi1y7m3yuxkejn38lk6pd6u3pg29h9repjnlwjwed

uguu10f
P
F
/cosmos.crypto.secp256k1.PubKey#
!,>���K�_��kHͨw�=��\��RY



uguu1222��test 
ethereum signature verification failed; mismatching recovered address and sender: 0x931377717aa8FD3a5Cb87ab859F93053985afD86 != 0x3088aB2B9E4E1c146De9400433cBe5a65242c5Cf8:32PM DBG rejected bad transaction err=null module=mempool peerID= res={"check_tx":{"code":4,"codespace":"sdk","data":null,"events":[],"gas_used":"51368","gas_wanted":"81448","info":"","log":"signature verification failed; please verify account number (2) and chain-id (test): unauthorized"}} tx="��\x17X\x11w��vlrT���\x17Fi�3��zK8�.��0Y�"
8:32PM DBG served RPC HTTP response duration=0 method=POST module=api-server remoteAddr=172.30.240.1:11821 status=200 url={"ForceQuery":false,"Fragment":"","Host":"","Opaque":"","Path":"/cosmos/tx/v1beta1/txs","RawFragment":"","RawPath":"","RawQuery":"","Scheme":"","User":null}

frontend app's log

simulatedResult {data: {…}, status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
tx-common.service.ts:226 {simulatedGasUsed: '74044', simulatedGasUsedWithMargin: '81448', simulatedFeeWithMarginNumber: 1221.72, simulatedFeeWithMargin: '1222'}
metamask.infrastructure.service.ts:281 {body: {…}, auth_info: {…}, signatures: Array(0)}
metamask.infrastructure.service.ts:114 chainId 5
metamask.infrastructure.service.ts:115 accounts ['0x6264c07ea0a1e7258d9186b7f7203c4cf88913a1']
metamask.infrastructure.service.ts:116 ethAddress 0x6264c07ea0a1e7258d9186b7f7203c4cf88913a1
metamask.infrastructure.service.ts:157 {domain: {…}, message: {…}, primaryType: 'Message', types: {…}}
metamask.infrastructure.service.ts:160 {"domain":{"chainId":5,"name":"UnUniFi","version":"1"},"message":{"auth_info":"{\"signer_infos\":[{\"public_key\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Axt5LD6Egc4PGxVtS9NfnYtrSM2od84BPYXkH1zR/1JZ\"},\"mode_info\":{\"single\":{\"mode\":1}},\"sequence\":0}],\"fee\":{\"amount\":[{\"denom\":\"uguu\",\"amount\":\"1222\"}],\"gas_limit\":81448,\"payer\":\"\",\"granter\":\"\"}}","body":"{\"messages\":[{\"@type\":\"/cosmos.bank.v1beta1.MsgSend\",\"amount\":[{\"denom\":\"uguu\",\"amount\":\"10\"}],\"from_address\":\"ununifi1xzy2k2u7fcwpgm0fgqzr8jl95efy93w0tsd4tr\",\"to_address\":\"ununifi1y7m3yuxkejn38lk6pd6u3pg29h9repjnlwjwed\"}],\"extension_options\":[],\"non_critical_extension_options\":[],\"memo\":\"\",\"timeout_height\":0}","chain_id":"test","account_number":"2"},"primaryType":"Message","types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"}],"Message":[{"name":"body","type":"string"},{"name":"auth_info","type":"string"},{"name":"chain_id","type":"string"},{"name":"account_number","type":"string"}]}}
metamask.infrastructure.service.ts:166 signature 0x9023abe4e3cae8f308aff996e8b141e3b147ab098b961bd1d7d82a6df1b58b714990fc8914b43f6ab74d5a66b5f98070cdccafe143485c62add932165aca316a1c
metamask.infrastructure.service.ts:294 {"domain":{"chainId":5,"name":"UnUniFi","version":"1"},"message":{"auth_info":"{\"signer_infos\":[{\"public_key\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Axt5LD6Egc4PGxVtS9NfnYtrSM2od84BPYXkH1zR/1JZ\"},\"mode_info\":{\"single\":{\"mode\":1}},\"sequence\":0}],\"fee\":{\"amount\":[{\"denom\":\"uguu\",\"amount\":\"1222\"}],\"gas_limit\":81448,\"payer\":\"\",\"granter\":\"\"}}","body":"{\"messages\":[{\"@type\":\"/cosmos.bank.v1beta1.MsgSend\",\"amount\":[{\"denom\":\"uguu\",\"amount\":\"10\"}],\"from_address\":\"ununifi1xzy2k2u7fcwpgm0fgqzr8jl95efy93w0tsd4tr\",\"to_address\":\"ununifi1y7m3yuxkejn38lk6pd6u3pg29h9repjnlwjwed\"}],\"extension_options\":[],\"non_critical_extension_options\":[],\"memo\":\"\",\"timeout_height\":0}","chain_id":"test","account_number":"2"},"primaryType":"Message","types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"}],"Message":[{"name":"body","type":"string"},{"name":"auth_info","type":"string"},{"name":"chain_id","type":"string"},{"name":"account_number","type":"string"}]}}
metamask.infrastructure.service.ts:295 0x9023abe4e3cae8f308aff996e8b141e3b147ab098b961bd1d7d82a6df1b58b714990fc8914b43f6ab74d5a66b5f98070cdccafe143485c62add932165aca316a1c
metamask.infrastructure.service.ts:298 9023abe4e3cae8f308aff996e8b141e3b147ab098b961bd1d7d82a6df1b58b714990fc8914b43f6ab74d5a66b5f98070cdccafe143485c62add932165aca316a1c
tx-common.service.ts:249 TxBuilder {sdk: CosmosSDK, txRaw: TxRaw}
bank.application.service.ts:129 Error: signature verification failed; please verify account number (2) and chain-id (test): unauthorized
    at tx-common.service.ts:259:13
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:25:1)
    at _ZoneDelegate.invoke (zone.js:372:1)
    at Object.onInvoke (core.js:28692:1)
    at _ZoneDelegate.invoke (zone.js:371:1)
    at Zone.run (zone.js:134:1)
    at zone.js:1275:1
    at _ZoneDelegate.invokeTask (zone.js:406:1)

@YasunoriMATSUOKA
Copy link
Contributor Author

Memo: Need to change from signTypedData_v4 to personal_sign

@YasunoriMATSUOKA
Copy link
Contributor Author

@jununifi @kimurayu45z
I changed the signing method from signTypedData_v4 to personal_sign and retry to send bank send tx.
As a result, same error is occuring.
I have added more details on notion link in slack comment.
So plz refer it.
Thank you.

Screenshot
ununifi-connect-metamask-and-send-bank-send-tx-sample_20220602

@YasunoriMATSUOKA YasunoriMATSUOKA changed the title [WIP] feat: metamask support feat: metamask support Jun 7, 2022
@YasunoriMATSUOKA
Copy link
Contributor Author

@jununifi cc: @kimurayu45z
I think MetaMask related implementation on the front end is complete.
Next, I would like to ask you to consider implementation of transaction signature verification on the back-end side.
If there are any problems, please let me know 🙏
Thank you for your continued support.
I will leave this PR in Draft status for now and I will focus other tasks.

const txJson = JSON.parse(txJsonString);

// fix JSONstringify issue
delete txJson.auth_info.signer_infos[0].mode_info.multi;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by using
cosmos-client/cosmos-client-ts#71
this PR, you can delete this line.

// fix JSONstringify issue
delete txJson.auth_info.signer_infos[0].mode_info.multi;
txJson.auth_info.signer_infos[0].mode_info.single.mode = 'SIGN_MODE_DIRECT';
txJson.auth_info.fee.gas_limit = txJson.auth_info.fee.gas_limit.toString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line also seems to be able to be removed.

Copy link
Contributor Author

@YasunoriMATSUOKA YasunoriMATSUOKA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @jununifi

Comment on lines +18 to +61
private messageToDigest(message: string): Uint8Array {
const messageHash = ethers.utils.hashMessage(message);
const digest = ethers.utils.arrayify(messageHash);
return digest;
}

private async personalSign(message: string): Promise<string> {
await detectEthereumProvider({ mustBeMetaMask: true });
if (!window.ethereum?.isMetaMask) {
throw Error('MetaMask is not installed!');
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const accounts = await provider.send('eth_requestAccounts', []);
if (!accounts?.length) {
throw Error('Failed to get MetaMask account!');
}
const address = accounts[0];
console.log(address);
const signer = provider.getSigner(address);
const signature = signer.signMessage(message);
return signature;
}

private async getPublicKeyFromMetaMask(): Promise<string> {
const message = 'Connecting to MetaMask from UnUniFi';
const signature = await this.personalSign(message);
console.log('signature', signature);
const recoveredAddress = ethers.utils.verifyMessage(message, signature);
console.log('ethRecoveredAddress', recoveredAddress);

const digest = this.messageToDigest(message);
console.log('digest', digest);
const recoveredUncompressedPublicKey = ethers.utils.recoverPublicKey(digest, signature);
console.log('ethUncompressedPublicKey', recoveredUncompressedPublicKey);
const recoverdAddress2 = ethers.utils.recoverAddress(digest, signature);
console.log('ethRecoveredAddress2', recoverdAddress2);
const recoveredCompressedPublicKey = ethers.utils.computePublicKey(
recoveredUncompressedPublicKey,
true,
);
console.log('ethCompressedPublicKey', recoveredCompressedPublicKey);
const publicKeyWithout0x = recoveredCompressedPublicKey.slice(2);
return publicKeyWithout0x;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jununifi
You can refer the procedure to get public key on front end side.

Copy link
Contributor Author

@YasunoriMATSUOKA YasunoriMATSUOKA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @jununifi

Comment on lines +78 to +94
private async getCosmosStoredWalletFromMetaMask(): Promise<StoredWallet> {
const ethAddress = await this.getEthAddress();
const publicKeyString = await this.getPublicKeyFromMetaMask();
const cosmosPublicKey = createCosmosPublicKeyFromString(KeyType.secp256k1, publicKeyString);
if (!cosmosPublicKey) {
throw Error('Invalid public key!');
}
const accAddress = cosmosclient.AccAddress.fromPublicKey(cosmosPublicKey);
const storedWallet: StoredWallet = {
id: ethAddress,
type: WalletType.metaMask,
key_type: KeyType.ethsecp256k1,
public_key: publicKeyString,
address: accAddress.toString(),
};
return storedWallet;
}
Copy link
Contributor Author

@YasunoriMATSUOKA YasunoriMATSUOKA Jun 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jununifi
You can refer the procedure to convert from public key to UnUniFi address.

@YasunoriMATSUOKA YasunoriMATSUOKA marked this pull request as ready for review June 13, 2022 08:32
@YasunoriMATSUOKA
Copy link
Contributor Author

@kimurayu45z
plz review 🙏

Copy link
Contributor

@kimurayu45z kimurayu45z left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for fixing config.js .

@YasunoriMATSUOKA YasunoriMATSUOKA merged commit 568081e into develop Jun 13, 2022
@YasunoriMATSUOKA YasunoriMATSUOKA deleted the feature/metamask-support branch June 13, 2022 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants